﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GrandwayLib.Meters.Interfaces;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Timers;
using System.Text.RegularExpressions;

namespace GrandwayLib.Meters.Classes
{
    /// <summary>
    /// 横河组合仪AQ2211
    /// </summary>
    public class OPM_AQ2211 : IOpticalPowerMeter
    {
        private bool isConnected = false; //是否已连接
        private SocketParam socketParam = new SocketParam("192.168.1.10",30000,SocketType.Client); //嵌套字参数
        private int slot = 3; //默认卡槽位置
        private int channel = 2; //默认通道号
        private Socket socket = new Socket(AddressFamily.InterNetwork,System.Net.Sockets.SocketType.Stream,ProtocolType.Tcp); //嵌套字对象
        private AutoResetEvent waveChangedEvent = new AutoResetEvent(false); //波长切换成功信号量
        private AutoResetEvent powerReadedEvent = new AutoResetEvent(false); //读取到光功信号量

        private System.Timers.Timer checkConnectTimer = new System.Timers.Timer(1000); //检查连接是否正常的定时器

        private double currentPower = -9999; //当前光功
        private int currentWavelength = 0; //当前波长

        /// <summary>
        /// 无参构造器
        /// </summary>
        public OPM_AQ2211()
        {
            checkConnectTimer.Elapsed += new ElapsedEventHandler(CheckConnectState);
        }

        /// <summary>
        /// 机型名称为AQ2211
        /// </summary>
        public string Name => "AQ2211";

        /// <summary>
        /// 连接参数类型为SocketParam
        /// </summary>
        public Type ParamType => typeof(SocketParam);

        /// <summary>
        /// 连接参数
        /// </summary>
        public object ConnectParam { 
            get => socketParam;
            set {
                if (value.GetType().Equals(ParamType))
                {
                    SocketParam param = value as SocketParam;
                    socketParam.IpAddress = param.IpAddress;
                    socketParam.PortNumber = param.PortNumber;
                }
            }
        }

        /// <summary>
        /// 是否在已连接状态
        /// </summary>
        public bool IsConnected => isConnected;

        /// <summary>
        /// 连接后异常断开产生一个事件
        /// </summary>
        public event EventHandler BreakConnect;

        /// <summary>
        /// 获取槽位
        /// </summary>
        /// <returns>所在槽位</returns>
        public int GetSlot()
        {
            return slot;
        }

        /// <summary>
        /// 设置槽位
        /// </summary>
        /// <param name="currentSlot"></param>
        public void SetSlot(int currentSlot) {
            slot = currentSlot;
        }

        /// <summary>
        /// 获取通道号
        /// </summary>
        /// <returns>通道号</returns>
        public int GetChannel()
        {
            return channel;
        }

        /// <summary>
        /// 设置通道号
        /// </summary>
        public void SetChannel(int currentChannel)
        {
            channel = currentChannel;
        }

        /// <summary>
        /// 切换到指定波长
        /// </summary>
        /// <param name="wavelength">波长</param>
        /// <returns>是否切换成功</returns>
        public bool ChangeWavelength(int wavelength)
        {
            string changeWaveOrder = ":SENSE" + slot + ":CHANNEL" + channel + ":POWER:WAVELENGTH " + wavelength + "NM\n";
            socket.Send(Encoding.UTF8.GetBytes("\n" + changeWaveOrder)); //切换波长
            string readWaveOrder = ":SENSE" + slot + ":CHANNEL" + channel + ":POWER:WAVELENGTH?\n";
            socket.Send(Encoding.UTF8.GetBytes("\n" + readWaveOrder)); //读取当前波长
            bool result = waveChangedEvent.WaitOne(1000); 
            if(!result || wavelength != currentWavelength) //1秒内收不到数据，或当前波长与传入波长不一致返回false
            {
                return false;
            }
            return result;
        }


        /// <summary>
        /// 连接Socket，PC作为客户端连接到AQ2211
        /// </summary>
        /// <returns>是否连接成功</returns>
        public bool Connect()
        {
            IPAddress address = IPAddress.Parse(socketParam.IpAddress);
            IPEndPoint endPoint = new IPEndPoint(address,socketParam.PortNumber);
            try
            {
                socket.Connect(endPoint);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                socket.Close();
            }catch(Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 读取当前光功值，如果读取失败，返回-9999.每次读取完光功后，仪表会自动切换为Single模式，需要发送命令切换为normal模式
        /// </summary>
        /// <returns>当前光功值，或-9999</returns>
        public double GetOpticalPower()
        {
            string changeToNormalOrder = ":INITIATE" + slot + ":CHANNEL" + channel + ":CONTINUOUS 1\n";
            socket.Send(Encoding.UTF8.GetBytes(changeToNormalOrder)); //发送命令，使光功处于连续测量模式
            Thread.Sleep(500);
            /* 读取光功率值 */
            string orderString = ":READ" + slot + ":CHANNEL" + channel + ":POWER?\n"; //命令待填充
            socket.Send(Encoding.UTF8.GetBytes(orderString)); //发送读取光功率命令
            bool result = powerReadedEvent.WaitOne(1000);
            socket.Send(Encoding.UTF8.GetBytes(changeToNormalOrder)); //发送命令，使光功处于连续测量模式
            if (!result)
            {
                return -9999;
            }
            return currentPower;
        }


        //定时检测连接状态
        private void CheckConnectState(object sender, ElapsedEventArgs arg)
        {
            try
            {
                byte[] tmp = new byte[1];
                socket.Send(tmp, 0, 0);
            }
            catch (SocketException e)
            {
                if (e.NativeErrorCode.Equals(10035))
                {
                    return;
                }
                BreakConnect?.Invoke(this, new EventArgs());  //如果连接异常，则产生一个事件
            }
        }

        //分析Socket收到的数据
        private void ReadData()
        {
            try
            {
                StringBuilder builder = new StringBuilder();
                while (true)
                {
                    byte[] buffer = new byte[1024];
                    int receiveLength = socket.Receive(buffer);
                    builder.Append(Encoding.UTF8.GetString(buffer,0,receiveLength));
                    string receiveString = builder.ToString();
                    int lastIndex = receiveString.LastIndexOf("\r"); //查找最后一个回车符
                    if (lastIndex > -1)
                    {
                        #region 截取最后一个回车符前面的数据分析(含回车符)，分析完成后，删除已分析完的数据
                        string data = receiveString.Substring(0,lastIndex + 1);
                        AnalysisData(data);
                        builder.Remove(0,lastIndex + 1);
                        #endregion
                    }
                }
            }
            catch { }
        }

        //分析收到的字符串
        private void AnalysisData(string data) {
            int index = -1;
            while ((index = data.IndexOf("\r")) > -1)
            {
                string pattern_s = "^[a-z],[A-Z],[0,9]";
                while (!Regex.IsMatch(data, pattern_s)) //如果不是字母或数字开头，移除一个字符
                {
                    data = data.Substring(1, data.Length - 1);
                }
                string s = data.Substring(0,index + 1);
                string pattern = @"^[+,-][0-9].[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]E[+,-][0-9][0-9][0-9]\r";
                if (!Regex.IsMatch(s, pattern)) //如果数据不符合上述格式，则直接清除上述字符串，并退出本次循环
                {
                    data = data.Substring(index,data.Length - index - 1);
                    continue;
                }
                string baseDataString = s.Substring(0, 7); //取0~7个字符作为基础数据
                string coeffString = s.Substring(s.Length - 4, 4); //取最后4个字符作为系数
                double baseData = Convert.ToDouble(baseDataString);
                double coeff = Math.Pow(10, Convert.ToInt16(coeffString)); //后4位表示10的n次方，其中n有正有负
                double result= baseData * coeff;
                if (coeff < 0.00001) //如果系数小于0.00001，认为返回值是波长。否则认为是功率值
                {
                    currentWavelength = (int)(result * Math.Pow(10,9.0));
                    waveChangedEvent.Set();
                }
                else
                {
                    currentPower = Math.Round(result,2);
                    powerReadedEvent.Set();
                }
                data = data.Substring(index, data.Length - index - 1); //清除已解析的数据
            }
        }
    }
}
